﻿Function Initialize-InfrastructureEnvironment
{
    [cmdletbinding(DefaultParameterSetName="none")]
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [ValidateScript({Test-Path $_ -PathType Leaf})] 
        [String] 
        $InfrastructureManifestFilePath,

        [Parameter(ParameterSetName="LoadTemplates", Mandatory=$true,ValueFromPipeline=$false)]
        [ValidateScript({Test-Path $_ -PathType Container})] 
        [String] 
        $TemplateRootDirectoryPath
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process 
    {
        $infrastructureManifest = New-Object PSObject -Property @{
                                                                   InfrastructureManifestFilePath=$InfrastructureManifestFilePath
                                                                   Status=$true
                                                                   Errors=@()
                                                                   Warnings=@()
                                                                   Templates=@{}
                                                                   InfrastructureInformation=(New-Object PSObject)
                                                                 }
        try
        {
            #Get full Path from TemplateFilePath argument
            $safeInfrastructureManifestFilePath = Resolve-Path -Path $InfrastructureManifestFilePath
            $infrastructureManifest.InfrastructureManifestFilePath = $safeInfrastructureManifestFilePath.ProviderPath

            #Select root node from template file
            $manifestRootNode = Select-Xml -Path $InfrastructureManifestFilePath -XPath "/*"
            $infrastructureManifest.InfrastructureInformation | Add-Member -MemberType NoteProperty ElementType -Value $manifestRootNode.Node.LocalName
            Add-XmlAttributesToObject -Source $manifestRootNode.Node -Target $infrastructureManifest.InfrastructureInformation

            $infrastructureTempObject = New-DynamicObject_v2 -Source $manifestRootNode.Node
            Clone-Member -Source $infrastructureTempObject -Target $infrastructureManifest -All

            if ($PSCmdlet.ParameterSetName -eq 'LoadTemplates')
            {
                Get-ChildItem -Path $TemplateRootDirectoryPath | ForEach-Object {
                    Add-Templates -platformConfiguration $infrastructureManifest -TemplateFilePath $_.FullName
                }
            }


        }
        catch [Exception]
        {
            $infrastructureManifest.status = $false
            $infrastructureManifest.Errors += $_
            Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Category Error -Message "Error" -AdditionalData @{Exception=$_}) | Out-Null
        }
        write-output $infrastructureManifest
    }
}

Function Add-Templates
{
    [cmdletbinding(DefaultParameterSetName="none")]
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $PlatformConfiguration,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [ValidateScript({Test-Path $_ -PathType Leaf})] 
        [String] 
        $TemplateFilePath
    )
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process
    {
        $summary = Import-TemplatesFromXmlFile -TemplateFile $TemplateFilePath -Templates $platformConfiguration.Templates
        if (!$summary.status)
        {
            Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Category Error -Message "Errors detected") | Out-Null
            $platformConfiguration.Status = $false
        }
        $platformConfiguration.Errors += $summary.Errors
        $platformConfiguration.Warnings += $summary.Warnings

    }
}

Function Add-ApplicationManifest
{
    [cmdletbinding(DefaultParameterSetName="none")]
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $PlatformConfiguration,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [ValidateScript({Test-Path $_ -PathType Leaf})] 
        [String] 
        $ApplicationManifestlFilePath
    )
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process
    {

        $applicationManifestRootNode = Select-Xml -Path $ApplicationManifestlFilePath -XPath "/*"
        $applicationManifest = New-DynamicObject_v2 -Source $applicationManifestRootNode.Node

        #Merge Datas et DataContexts
        $platformConfiguration.configuration.datas = Merge-HashTable -default $platformConfiguration.configuration.datas -uppend $applicationManifest.configuration.datas
        $platformConfiguration.configuration.dataContexts = Merge-HashTable -default $platformConfiguration.configuration.dataContexts -uppend $applicationManifest.configuration.dataContexts
        $platformConfiguration | Add-Member -MemberType NoteProperty -Name DeploymentModels -Value $applicationManifest.DeploymentModels

    }
}


Function Load-ManifestServers
{
        [cmdletbinding(DefaultParameterSetName="none")]
        Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $PlatformConfiguration
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process 
    {

        #Browse Servers from platform model
        $PlatformConfiguration.Servers.Values | ForEach-Object {
            
            try
            {
                Load-ManifestServer -PlatformConfiguration $PlatformConfiguration -ServerDeclaration $_
            }
            catch [Exception]
            {
                $PlatformConfiguration.status = $false
                $PlatformConfiguration.Errors += $_
                Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Category Error -Message "Error" -AdditionalData @{Exception=$_}) | Out-Null
            }

        }
    }
}

Function Load-ManifestServer
{
    [cmdletbinding(DefaultParameterSetName="none")]
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $PlatformConfiguration,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $ServerDeclaration
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process 
    {

        $server = New-ManifestServer

        #Retrieve the server model
        if (!($PlatformConfiguration.ServerModels.ContainsKey($ServerDeclaration.Model))) {throw "Server Model $($ServerDeclaration.Model) doesn't exist in the server models configuration"}
        $serverModel = $PlatformConfiguration.ServerModels.$($ServerDeclaration.Model)

        #Retrieve the template from server Model
        $serverTemplate = Find-Template -Templates $PlatformConfiguration.Templates -TemplateType Server -TemplateUniqueKey $serverModel.Template

        #Retrieve the data context for the server
        if (!($PlatformConfiguration.configuration.dataContexts.ContainsKey($serverModel.dataContext))) {throw "DataContext $($serverModel.dataContext) doesn't exist in the datacontext collection"}
        $dataContext = $PlatformConfiguration.configuration.dataContexts.$($serverModel.dataContext)

        #Bind DataContext to Server Model
        $boundServerModel = ConvertTo-Instance -Template $serverTemplate -DataContext $dataContext

        #Apply bound Server to manifest server
        Merge-MemberToManifestObject -sourceObject $boundServerModel -targetObject $server -PlatformConfiguration $PlatformConfiguration

        #Add server datacontext property in server
        $server | Add-Member -MemberType NoteProperty -Name DataContext -Value $serverModel.DataContext

        #Apply Server Information to manifest server
        Merge-MemberToManifestObject -sourceObject $ServerDeclaration -targetObject $server -PlatformConfiguration $PlatformConfiguration

        #Load Middleware
        Load-ManifestMiddleware -PlatformConfiguration $PlatformConfiguration -ServerModel $serverModel -ServerManifest $server

        #Add the Platform configuration to the server
        $server | Add-Member -MemberType NoteProperty -Name PlatformConfiguration -Value $PlatformConfiguration

        $server


    }
}


Function Load-ManifestMiddleware
{
        [cmdletbinding(DefaultParameterSetName="none")]
        Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $PlatformConfiguration,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject]
        $ServerModel,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject]
        $ServerManifest

        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process 
    {

        #Browse Servers from platform model
        $ServerModel.middlewares.Values | ForEach-Object {
            
            $middlewareModel = $_

            #Retrieve the template from middleware Model
            $middlewareTemplate = Find-Template -Templates $PlatformConfiguration.Templates -TemplateType Middleware -TemplateUniqueKey $middlewareModel.Template
            if (-not $middlewareTemplate) {throw "Middleware template $($middlewareModel.Template) doesn't exist"}
            if (-not $middlewareTemplate.Template) {throw "Middleware template $($middlewareModel.Template) doesn't have the template property"}

            #Find existing the Middleware
            $currentMiddlewareManifest = Find-ObjectInCollection -Container $ServerManifest -PropertyName Middlewares -Uniquekey $middlewareModel.Template

            if (-not $currentMiddlewareManifest)
            {
                $currentMiddlewareManifest = New-ManifestMiddleware
                #Template key become the unique key of Middleware
                $currentMiddlewareManifest | Add-Member -MemberType NoteProperty -Name UniqueKey -Value $middlewareModel.Template
                $currentMiddlewareManifest | Add-Member -MemberType NoteProperty -Name Parent -Value $ServerManifest
                $currentMiddlewareManifest | Add-Member -MemberType NoteProperty -Name Level -Value $ServerManifest.ElementType -Force
                $currentMiddlewareManifest | Add-Member -MemberType NoteProperty -Name ElementType -Value $middlewareModel.ElementType -Force

                #Push object Middleware to servers
                Push-ObjectToCollection -Container $ServerManifest -PropertyName Middlewares -CurrentObject $currentMiddlewareManifest

                #Add all existing Members except the template
                Merge-MemberToManifestObject -sourceObject $middlewareTemplate -targetObject $currentMiddlewareManifest -PlatformConfiguration $PlatformConfiguration -Filter {(($_.name -ne 'BindingSource') -and ($_.name -ne 'BindingCollections') -and ($_.name -ne 'Template'))}
                $currentMiddlewareManifest | Add-Member -MemberType NoteProperty -Name Level -Value $ServerManifest.ElementType -Force
                $currentMiddlewareManifest | Add-Member -MemberType NoteProperty -Name ElementType -Value $middlewareModel.ElementType -Force

            }


            #Retrieve the data context for the server
            if (!($PlatformConfiguration.configuration.dataContexts.ContainsKey($middlewareModel.dataContext))) {throw "DataContext $($middlewareModel.dataContext) doesn't exist in the datacontext collection"}
            $dataContext = $PlatformConfiguration.configuration.dataContexts.$($middlewareModel.dataContext)


            #Bind DataContext to Middleware Model at executionContainer level
            if ($middlewareTemplate.Template.ExecutionContainer)
            {
                $boundExecutionContainer = ConvertTo-Instance -Template $middlewareTemplate.Template.ExecutionContainer -DataContext $dataContext

                #Apply bound executionContainer to manifest executionContainer
                $manifestExecutionContainer = New-ManifestExecutionContainer
                $manifestExecutionContainer | Add-Member -MemberType NoteProperty -Name Parent -Value $currentMiddlewareManifest
                Merge-MemberToManifestObject -sourceObject $boundExecutionContainer -targetObject $manifestExecutionContainer -PlatformConfiguration $PlatformConfiguration

                #Apply middleare model Information to manifest execution Container
                Merge-MemberToManifestObject -sourceObject $middlewareModel -targetObject $manifestExecutionContainer -PlatformConfiguration $PlatformConfiguration

                $manifestExecutionContainer | Add-Member -MemberType NoteProperty -Name Level -Value $currentMiddlewareManifest.ElementType -Force
                $manifestExecutionContainer | Add-Member -MemberType NoteProperty -Name ElementType -Value $middlewareTemplate.Template.ExecutionContainer.ElementType -Force

                Push-ObjectToCollection -Container $currentMiddlewareManifest -PropertyName ExecutionContainers -CurrentObject $manifestExecutionContainer
            }


            #Bind DataContext to Middleware Model at runtime level
            if ($middlewareTemplate.Template.Runtime)
            {
                $boundRuntime = ConvertTo-Instance -Template $middlewareTemplate.Template.Runtime -DataContext $dataContext

                #Apply bound Runtime to manifest Runtime
                $manifestRuntime = New-Object PSObject
                $manifestRuntime | Add-Member -MemberType NoteProperty -Name Parent -Value $currentMiddlewareManifest
                Merge-MemberToManifestObject -sourceObject $boundRuntime -targetObject $manifestRuntime -PlatformConfiguration $PlatformConfiguration

                #Apply middleare model Information to manifest runtime Container
                Merge-MemberToManifestObject -sourceObject $middlewareModel -targetObject $manifestRuntime -PlatformConfiguration $PlatformConfiguration

                $manifestRuntime | Add-Member -MemberType NoteProperty -Name Level -Value $currentMiddlewareManifest.ElementType -Force
                $manifestRuntime | Add-Member -MemberType NoteProperty -Name ElementType -Value $middlewareTemplate.Template.Runtime.ElementType -Force

                Push-ObjectToCollection -Container $currentMiddlewareManifest -PropertyName Runtimes -CurrentObject $manifestRuntime
            }


        }


    }
}

Function Load-ManifestComponents
{
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $ApplicationInstance,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $PlatformConfiguration,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $SourceNode,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $TargetServer
        )
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process
    {

        #Browse the source Nodes
        $SourceNode.Components.values | foreach-object {
            
            #Create the new component manifest Object
            $ComponentManifest = New-ManifestComponent
            $ComponentManifest | Add-Member -MemberType NoteProperty -Name Application -Value $ApplicationInstance
            
            #Find the Component
            $Component = $ApplicationInstance.Components.$($_.uniqueKey)
            if (-not $Component) {throw "Component '$($_.UniqueKey)' doesn't exist on application instance '$($ApplicationInstance.uniqueKey)'"}

            #Merge Component Node on Component Manifest
            Merge-MemberToManifestObject -sourceObject $_ -targetObject $ComponentManifest -PlatformConfiguration $PlatformConfiguration

            #Link the component to its execution container on the TargetedServer
            Join-ComponentsToServer -Component $ComponentManifest -TargetServer $TargetServer

            #Merge Component Template on Component Manifest
            Merge-MemberToManifestObject -sourceObject $Component -targetObject $ComponentManifest -PlatformConfiguration $PlatformConfiguration


        }

    }
}


function Join-ComponentsToServer
{
    [cmdletbinding(DefaultParameterSetName="none")]    
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $Component,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $TargetServer
        )
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process
    {
        #Find the Middleware
        $middleware = $TargetServer.Middlewares.$($Component.middleware)
        if (-not $middleware) {throw "Middleware '$($Component.middleware)' doesn' exist on server '$($TargetServer.UniqueKey)'"}
        Write-Debug "Join-ComponentsToServer - Find the Middleware : $middleware"

        #Find the component execution Container
        $executionContainer = $middleware.executionContainers.$($Component.executionContainer)
        if (-not $executionContainer) {throw "Execution Container '$($Component.executionContainer)' doesn't exist on middleware '$($Component.middleware)' on server '$($TargetServer.UniqueKey)'"}

        $Component | Add-Member -MemberType NoteProperty -Name Parent -Value $executionContainer
        $Component | Add-Member -MemberType NoteProperty -Name Level -Value $executionContainer.ElementType -Force

        #Find the component runtime
        if ($Component.runtime)
        {
            $runtime = $middleware.runtimes.$($Component.runtime)
            if (-not $runtime) {throw "Component runtime '$($Component.runtime)' doesn't exist on middleware '$($Component.middleware)' on server '$($TargetServer.UniqueKey)'"}
            $Component | Add-Member -MemberType NoteProperty -Name Runtime -Value $runtime
        }

        #Add the component to its execution Container
        Push-ObjectToCollection -Container $executionContainer -PropertyName Components -CurrentObject $Component



    }
}


function Init-DeploymentTarget
{
    [cmdletbinding(DefaultParameterSetName="none")]    
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $DeploymentModel,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PSObject] 
        $PlatformConfiguration
        )
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -Message "Start" -AdditionalData $PSBoundParameters)
    }
    End
    {
        $watch.Stop()
        $eventId = Publish-ToMiddlewareLogs -Log (Get-LogMessage -Command $PSCmdlet.MyInvocation.MyCommand -UniqueId $eventId -Message "End" -AdditionalData @{Duration=$watch.Elapsed})
    }
    Process
    {

        #Create the deploymentTarget
        $DeploymentTarget = New-ManifestDeploymentTarget
        $DeploymentTarget | Add-Member -MemberType NoteProperty -Name PlatformConfiguration -Value $PlatformConfiguration
        Clone-Member -Source $DeploymentModel -Target $DeploymentTarget
        Write-Debug "Init-DeploymentTarget : $DeploymentTarget"

        #Find the application template
        $applicationTemplate = Find-Template -Templates $PlatformConfiguration.Templates -TemplateType Application -TemplateUniqueKey $DeploymentTarget.application
        if (-not $applicationTemplate) {throw "Application '$($DeploymentTarget.application)' doesn' exist on platform confifuration templates"}
        Write-Debug "Application template : $applicationTemplate"

        #Find the DataContext
        $dataContext = $PlatformConfiguration.configuration.dataContexts.($DeploymentTarget.dataContext)
        if (-not $dataContext) {throw "Data context '$($DeploymentTarget.dataContext)' doesn't exist in the platform configuration"}

        #Create the application Instance
        $applicationInstance = ConvertTo-Instance -Template $applicationTemplate -DataContext $dataContext
        Clone-Member -Source $applicationInstance -Target $DeploymentTarget -All -Filter {$_.name -notin 'uniqueKey','level','ElementType'}


        #Browse deployment Target nodes and join components definition to the server
        foreach($node in $DeploymentModel.deploymentNodes.Values)
        {
            Write-Debug "Start implementation node components : $node"
            
            #Retrieve the declaration server for this node
            $serverDeclaration = $PlatformConfiguration.servers.$($node.server)
            if (-not $serverDeclaration) {throw "Server '$($node.server)' found on node '$($node.UniqueKey)' doesn't exist on the manifest servers collection"}
            Write-Debug "Target server for this node : $serverDeclaration"

            #Create an instance of Manifest server for this node
            $manifestServer = Load-ManifestServer -PlatformConfiguration $PlatformConfiguration -ServerDeclaration $serverDeclaration
            $manifestServer | Add-Member -MemberType NoteProperty -Name Parent -Value $DeploymentTarget
            $manifestServer | Add-Member -MemberType NoteProperty -Name Container -Value $DeploymentTarget

            #link the Manifest server to the deployment Target
            $DeploymentTarget.Servers.Add($manifestServer.UniqueKey, $manifestServer) 

            #Join components from node to manifest servers
            Load-ManifestComponents -ApplicationInstance $applicationInstance -PlatformConfiguration $PlatformConfiguration -SourceNode $node -TargetServer $manifestServer

        }

        $DeploymentTarget

    }
}



<#NOT USE #>

<#
Function Initialize-Environment_v2
{
        [cmdletbinding(DefaultParameterSetName="none")]
        Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [ValidateScript({Test-Path $_ -PathType Leaf})] 
        [String] 
        $ApplicationManifestPath
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Initialize-Environment from $DeploymentTarget and from $ActionsList"
    }
    End
    {
        write-verbose "End Initialize-Environment from $DeploymentTarget and from $ActionsList"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process 
    {

        $applicationManifestNode = Select-Xml -Path $ApplicationManifestPath -XPath "/applicationManifest"

        #Load folders template
        $Folders = @{}
        $applicationManifestNode.Node.templates.SelectNodes("folders/folder") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "uniqueKey"
            $Folders.Add($_.UniqueKey, $_)
        }


        #Load middleware template
        $Middlewares = @{}
        $applicationManifestNode.Node.templates.SelectNodes("middlewares/middleware") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "uniqueKey"
            $Middlewares.Add($_.UniqueKey, $_)
        }


        #Load server template
        $Servers = @{}
        $applicationManifestNode.Node.templates.SelectNodes("servers/server") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "uniqueKey"
            $Servers.Add($_.UniqueKey, $_)
        }

        #Load binaries template
        $BinaryTemplates = @{}
        $applicationManifestNode.Node.binaries.templates.SelectNodes("*") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "uniqueKey"
            $BinaryTemplates.Add($_.UniqueKey, $_)

        }

        #Load components list
        $BinaryComponents = @{}
        $applicationManifestNode.Node.binaries.SelectNodes("components/component") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "name"
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "version"
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "template"
            $uniqueKey=[String]::Format("{0}_{1}",$_.name, $_.version)
            $BinaryComponents.Add($uniqueKey, $_)
        }


        #Load products list
        $BinaryProducts = @{}
        $applicationManifestNode.Node.binaries.SelectNodes("products/product") | ForEach-Object {
        #    Assert-XmlAttribute -xmlElement $_ -xmlAttribute "name"
        #    Assert-XmlAttribute -xmlElement $_ -xmlAttribute "version"
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "uniqueKey"
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "template"
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "template"
        #    $uniqueKey=[String]::Format("{0}_{1}",$_.name, $_.version)
        #    $BinaryProducts.Add($uniqueKey, $_)
            $BinaryProducts.Add($_.uniqueKey, $_)
        }


        #Load Binaries Repositories
        $BinariesRepositories = @{}
        $applicationManifestNode.Node.binaries.SelectNodes("binariesRepositories/binariesRepository") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "uniqueKey"
            $binariesRepository = ConvertFrom-TemplateBinaryRepository -xmlElement $_
            $BinariesRepositories.Add($_.uniqueKey, $binariesRepository)
        }



        #Load Configuration Repositories
        $ConfigurationRepositories = @{}
        $applicationManifestNode.Node.configuration.SelectNodes("configurationRepositories/configurationRepository") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "uniqueKey"
            $configurationRepository = ConvertFrom-TemplateConfigurationRepository -xmlElement $_
            $ConfigurationRepositories.Add($_.uniqueKey, $configurationRepository)
        }


        #Create the configuration model
        $Configuration = New-Object PSObject -Property @{
                                                        ConfigurationRepositories=$ConfigurationRepositories
                                                        Data=@{}
                                                       }

        #Load configuration data
        $applicationManifestNode.Node.configuration.SelectNodes("datas/data") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "uniqueKey"
            $configurationData = New-DynamicObject -Source $_
            $configuration.Data.Add($configurationData.UniqueKey, $configurationData)
        }


        #Create the templates model
        $templates = New-Object PSObject -Property @{
                                                    Folders=$Folders
                                                    Middlewares=$Middlewares
                                                    Components=$Components
                                                    Servers=$Servers
                                                    }

        #Create the binary repository model
        $binaryRepository = New-Object PSObject -Property @{
                                                            BinariesRepositories=$BinariesRepositories
                                                            Components=$BinaryComponents
                                                            Product=$BinaryProducts
                                                            Templates=$BinaryTemplates
                                                            }

        #Create the server lists
        $ServerList = @{}
        $applicationManifestNode.Node.servers.SelectNodes("server") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "hostName"
            $ServerList.Add($_.HostName, $_)    
        }
                                                        

        #Load application template
        $Applications = @{}
        $applicationManifestNode.Node.SelectNodes("applications/application") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "uniqueKey"
            $Applications.Add($_.UniqueKey, $_)
        }

        #Create the Application Manifest
        $global:ApplicationManifest = New-Object PSObject -Property @{
                                                                      Templates=$templates
                                                                      Binaries=$binaryRepository
                                                                      Servers=$ServerList
                                                                      Configuration=$Configuration
                                                                      Applications=$Applications
                                                                      }

        
        $deploymentTarget = ConvertFrom-DeploymentTarget -xmlElement $applicationManifestNode.Node.deploymentTargets
        $global:ApplicationManifest | Add-Member -MemberType NoteProperty -Name DeploymentTargets -Value $DeploymentTarget

    }
}


Function New-DynamicObject
{
        Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [System.Xml.XmlElement] 
        $Source
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start New-DynamicObject from $($Source.LocalName)"
    }
    End
    {
        write-verbose "End New-DynamicObject from $($Source.LocalName)"
        $watch.Stop();
        Write-Verbose "New-DynamicObject duration : $($watch.Elapsed)"
    }
    Process 
    {

        #Add mandatory properties
        $newObj = New-Object PSObject
        $newObj | Add-Member -MemberType NoteProperty ElementType -Value $Source.LocalName       

        Add-XmlAttributesToObject -Source $Source -Target $newObj
        
        Write-Output $newObj
    }
}


Function Initialize-EnvironmentConfiguration
{
        [cmdletbinding(DefaultParameterSetName="none")]
        Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [ValidateScript({Test-Path $_ -PathType Leaf})] 
        [String] 
        $ConfigurationManifestPath
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Initialize-Environment from $DeploymentTarget and from $ActionsList"
    }
    End
    {
        write-verbose "End Initialize-Environment from $DeploymentTarget and from $ActionsList"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process 
    {
        $applicationManifestNode = Select-Xml -Path $ConfigurationManifestPath -XPath "/datas"

        #Load configuration data
        $applicationManifestNode.Node.SelectNodes("data") | ForEach-Object {
            Assert-XmlAttribute -xmlElement $_ -xmlAttribute "uniqueKey"
            $configurationData = New-DynamicObject -Source $_

            if (-not $global:ApplicationManifest.configuration.Data.ContainsKey($configurationData.UniqueKey))
            {
                $global:ApplicationManifest.configuration.Data.Add($configurationData.UniqueKey, $configurationData)
            }
            else
            {
                write-waring "Override existing key $($configurationData.UniqueKey) from main application manifest file with value from the specific configuration file $ConfigurationManifestPath"
                $global:ApplicationManifest.configuration.Data.$($configurationData.UniqueKey) = $configurationData
            }
        }
        

    }
}




function Add-MemberToManifestObject
{
   [cmdletbinding(DefaultParameterSetName="none")]
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PsObject] 
        $ManifestObject,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [String] 
        $MemberName,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [Object] 
        $MemberValue

        )
    Process
    {
        #Check member Name already exist or not
        $existingMember = $ManifestObject | Get-Member -Name $MemberName
        if ($existingMember)
        {
            Write-Warning "Not yet implemented : existing Member $MemberName found on object '$($ManifestObject.ElementType)' identified by '$($ManifestObject.UniqueKey)'"
        }
        else
        {
            $ManifestObject | Add-Member -MemberType NoteProperty -Name $MemberName -Value $MemberValue
        }
    }
}

Function Import-Models
{
        [cmdletbinding(DefaultParameterSetName="none")]
        Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [ValidateScript({Test-Path $_ -PathType Leaf})] 
        [String] 
        $ModelFile
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Import-Models from '$ModelFile'"
    }
    End
    {
        write-verbose "End Import-Models from '$ModelFile'"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process 
    {

        #Select root node from template file
        $ModelRootNode = Select-Xml -Path $ModelFile -XPath "/*"
        New-DynamicObject_v2 -Source $ModelRootNode.Node


    }
}



#>